<?php
declare(strict_types=1);

namespace Zms\Basics;

use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Relations\HasOne;
use JetBrains\PhpStorm\ArrayShape;
use Zms\Paging\Paging;
use Zms\Unit\Arr;
use Zms\Unit\Is;

abstract class BasicsSearch
{
    /**
     * 搜索条件
     * @var array|mixed
     */
    protected array $where = [];

    /**
     * 字段栏目
     * @var array|string[]
     */
    protected array $column = ['*'];


    /**
     * 获取关联字段
     * @var array|mixed
     */
    protected array $with = [];
    
    /**
     * 页码
     * @var int
     */
    protected int $page = 1;

    /**
     * 分页大小
     * @var int
     */
    protected int $size = 15;

    /**
     * 排序
     * @var string
     */
    protected string $order = 'id DESC';

    public function __construct(
        #[ArrayShape([
            'where' => 'array',
            'page' => 'int',
            'size' => 'int',
            'order' => 'string',
            'column' => 'array',
            'with' => 'array'
        ])]
        array $config
    )
    {

        $this->where = $config['where'] ?? $this->where;
        $this->page = intval($config['page'] ?? $this->page);
        $this->size = intval($config['size'] ?? $this->size);
        $this->order = $config['order'] ?? 'id DESC';
        $this->column = ($config['column'] ?? $this->column) ?: ['*'];
        $this->with = $config['with'] ?? $this->with;
    }


    /**
     * 搜索
     * @return array
     */
    public function search(): array
    {
        $paging = new Paging($this->count(), $this->size, $this->page);
        $query = $this->query();
        if (count($this->with)) {
            $withs = [];
            foreach ($this->with as $withkey => $with) {
                //自然数组元素,代表没有指定关联配置,直接使用
                if (is_numeric($withkey)) {
                    $withs[] = $with;
                    continue;
                }
                //指定配置关联
                if (is_string($withkey) && $withkey) {
                    if (is_array($with) && Is::list($with)) {//指定字段
                        $withs[$withkey] = function (HasOne $one) use ($with) {
                            $one->select($with);
                        };
                    } elseif (is_callable($with)) {
                        $withs[$withkey] = $with;
                    }
                }
            }
            $query->with($withs);
        }
        $data = $query->select($this->column())->orderByRaw($this->order())->offset($paging->offset)->limit($paging->limit)->get()->toArray();
        if ($paging->total > 0) {
            $data = $this->HanderData($data);
        }
        return ['list' => $data, 'total' => $paging->total, 'current' => $paging->page, 'size' => $paging->size];
    }


    /**
     * 查询构造器
     * @return Builder
     */
    abstract public function query(): Builder;


    /**
     * 导出数据构造器
     * @param string $filename 文件名 不带文件后缀
     * @return array
     */
    public function export(string $filename): array
    {
        return [];
    }


    /**
     * 获取数据条数
     * @param string $columus
     * @return int
     */
    public function count(string $columus = 'id'): int
    {
        return $this->query()->count($columus);
    }


    /**
     * 获取排序
     * @return string
     */
    public function order(): string
    {
        return $this->order;
    }


    /**
     * 栏目获取
     * @return array
     */
    public function column(): array
    {
        return $this->column;
    }


    /**
     * 数据格式化
     * @param array $data
     * @return array
     */
    public function format(mixed $data): mixed
    {
        return $data;
    }


    /**
     * 处理数据
     * @param array $data
     * @return array
     */
    public function HanderData(array $data): array
    {
        foreach ($data as $index => $value) {
            $data[$index] = $this->format($value);
        }
        return $data;
    }


    /**
     * 获取搜搜条件
     * @param string $key
     * @return array|mixed|null
     */
    public function where(string $key): mixed
    {
        $val = Arr::get($this->where, $key);
        return Is::notEmpty($val) ? $val : null;
    }
}